package com.gg.douyin.sdk.service.impl;

import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.gg.douyin.sdk.bean.DouyinConfigStorage;
import com.gg.douyin.sdk.bean.param.ClientToken;
import com.gg.douyin.sdk.bean.param.GetAccessToken;
import com.gg.douyin.sdk.bean.param.GetUserInfo;
import com.gg.douyin.sdk.bean.param.OauthConnect;
import com.gg.douyin.sdk.bean.param.RefreshToken;
import com.gg.douyin.sdk.bean.param.SendMessage;
import com.gg.douyin.sdk.bean.param.UploadMedia;
import com.gg.douyin.sdk.bean.param.VideoCommentList;
import com.gg.douyin.sdk.bean.param.VideoCommentReply;
import com.gg.douyin.sdk.bean.param.VideoData;
import com.gg.douyin.sdk.bean.param.VideoList;
import com.gg.douyin.sdk.bean.result.AccessTokenResult;
import com.gg.douyin.sdk.bean.result.BaseResult;
import com.gg.douyin.sdk.bean.result.ClientTokenResult;
import com.gg.douyin.sdk.bean.result.DouyinResult;
import com.gg.douyin.sdk.bean.result.GetUserInfoResult;
import com.gg.douyin.sdk.bean.result.MediaUploadResult;
import com.gg.douyin.sdk.bean.result.VideoCommentListResult;
import com.gg.douyin.sdk.bean.result.VideoListResult;
import com.gg.douyin.sdk.exception.DouyinErrorCode;
import com.gg.douyin.sdk.exception.DouyinException;
import com.gg.douyin.sdk.service.DouyinService;
import com.gg.douyin.sdk.util.FileUtils;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.UUID;

/**
 *
 * @author guiguan
 * @date 2019/12/10 9:12
 */
@Slf4j
@Service
public class DouyinServiceImpl implements DouyinService {

    private volatile DouyinConfigStorage douyinConfigStorage;

    @Override
    public void init(DouyinConfigStorage douyinConfigStorage) {
        this.douyinConfigStorage = douyinConfigStorage;
    }

    /**
     * 获取code，code只能使用一次，用于获取accessToken
     *
     * @param oauthConnect OauthConnect
     */
    @Override
    public String getCode(OauthConnect oauthConnect) {
        return DOUYIN_API_PREFIX.concat(OAUTH_CONNECT).concat("?client_key=").concat(oauthConnect.getClientKey()).concat("&response_type=code").concat("&scope=").concat(oauthConnect.getScope()).concat("&redirect_uri=").concat(oauthConnect.getRedirectUri());
    }

    /**
     * 获取accessToken 15天过期
     *
     * @param getAccessToken GetAccessToken
     * @return AccessTokenResult
     */
    @Override
    public AccessTokenResult getAccessToken(GetAccessToken getAccessToken, RedisTemplate<String, String> RedisTemplate) throws DouyinException {
        JSONObject jsonObject = JSONObject.parseObject(JSON.toJSONString(getAccessToken));
        log.info("getAccessToken getAccessToken:{}", jsonObject);
        String result = HttpUtil.get(DOUYIN_API_PREFIX.concat(GET_ACCESS_TOKEN), jsonObject, 1000);
        log.info("getAccessToken result:{}", result);
        DouyinResult douyinResult = JSONObject.parseObject(result, DouyinResult.class);
        AccessTokenResult accessTokenResult = JSONObject.parseObject(douyinResult.getData(), AccessTokenResult.class);
        if (accessTokenResult.isNotSuccess()) {
            throw new DouyinException(accessTokenResult.getDescription());
        }
        if (null == this.douyinConfigStorage) {
            douyinConfigStorage = new DouyinConfigStorage(RedisTemplate);
        }
        this.douyinConfigStorage.putAccessToken(accessTokenResult.getOpenId(), accessTokenResult.getAccessToken(), accessTokenResult.getRefreshToken(), accessTokenResult.getExpiresIn());
        return accessTokenResult;
    }


    /**
     * 两种情况：
     * 1.accessToken未过期，刷新token相当于续期
     * 2.若accessToken已经过期，刷新token后获取一个新的accessToken和新的超时时间
     * <p>
     * 刷新accessToken 刷新后30天过期
     * 由于抖音获取评论，发送私信有限额，建议使用定时任务拉取,频率建议每天一次
     *
     * @param refreshToken RefreshToken
     * @return AccessTokenResult
     */
    @Override
    public AccessTokenResult refreshAccessToken(RefreshToken refreshToken) throws DouyinException {
        JSONObject jsonObject = JSONObject.parseObject(JSON.toJSONString(refreshToken));
        log.info("getAccessToken getAccessToken:{}", jsonObject);
        String result = HttpUtil.get(DOUYIN_API_PREFIX.concat(REFRESH_ACCESS_TOKEN), jsonObject, 1000);
        log.info("result:{}", result);
        DouyinResult douyinResult = JSONObject.parseObject(result, DouyinResult.class);
        AccessTokenResult accessTokenResult = JSONObject.parseObject(douyinResult.getData(), AccessTokenResult.class);
        if (accessTokenResult.isNotSuccess()) {
            throw new DouyinException(accessTokenResult.getDescription());
        }
        //更新缓存中的accessToken数据
        this.douyinConfigStorage.putAccessToken(this.douyinConfigStorage.getOpenId(), accessTokenResult.getAccessToken(), accessTokenResult.getRefreshToken(), accessTokenResult.getExpiresIn());
        return accessTokenResult;
    }

    @Override
    public ClientTokenResult getClientToken(ClientToken clientToken) throws DouyinException {
        JSONObject jsonObject = JSONObject.parseObject(JSON.toJSONString(clientToken));
        String result = HttpUtil.get(DOUYIN_API_PREFIX.concat(CLIENT_TOKEN), jsonObject, 1000);
        DouyinResult douyinResult = JSONObject.parseObject(result, DouyinResult.class);
        String data = douyinResult.getData();
        if (StringUtils.isBlank(data)) {
            ClientTokenResult clientTokenResult = new ClientTokenResult();
            clientTokenResult.setDescription("data is null");
            return clientTokenResult;
        }
        ClientTokenResult clientTokenResult = JSONObject.parseObject(douyinResult.getData(), ClientTokenResult.class);
        if (clientTokenResult.isNotSuccess()) {
            throw new DouyinException(clientTokenResult.getDescription());
        }
        return clientTokenResult;
    }


    private String getUrlParameter(String openId) throws DouyinException {
        String accessToken;
        if (null == this.douyinConfigStorage) {
            throw new DouyinException("please init this service with DouyinConfigStorage first;使用DouyinService之前请先调用init方法");
        }
        boolean needRefresh = this.douyinConfigStorage.needRefreshToken(openId);
        if (needRefresh) {
            String refreshTokenStr = this.douyinConfigStorage.getRefreshToken(openId);
            RefreshToken refreshToken = new RefreshToken();
            refreshToken.setClientKey(this.douyinConfigStorage.getClientKey());
            refreshToken.setRefreshToken(refreshTokenStr);
            refreshToken.setGrantType("authorization_code");
            AccessTokenResult accessTokenResult = this.refreshAccessToken(refreshToken);
            if (accessTokenResult.getErrorCode().equals(DouyinErrorCode.REFRESH_TOKEN_EXPIRED)) {
                //需要重新登录
                OauthConnect oauthConnect = OauthConnect.builder().clientKey(this.douyinConfigStorage.getClientKey()).redirectUri(this.douyinConfigStorage.getRedirectUri()).scope(this.douyinConfigStorage.getScope()).build();
                //二维码链接
                String code = this.getCode(oauthConnect);
                throw new DouyinException("授权已过期，请点击此链接，使用对应抖音号扫该页面上的二维码或者输入账号密码进行授权 链接:" + code);
            }
            accessToken = accessTokenResult.getAccessToken();
        } else {
            accessToken = this.douyinConfigStorage.getAccessToken(this.douyinConfigStorage.getOpenId());
        }
        if (StringUtils.isBlank(accessToken)) {
            throw new DouyinException("授权已过期，请重新授权");
        }
        return "?open_id=" + openId + "&access_token=" + accessToken;
    }

    /* 用户相关*/

    /**
     * 获取用户信息
     *
     * @param getUserInfo GetUserInfo
     * @return GetUserInfoResult
     * @throws DouyinException DouyinException
     */
    @Override
    public GetUserInfoResult userInfo(GetUserInfo getUserInfo) throws DouyinException {
        String urlParameter = getUrlParameter(getUserInfo.getOpenId());
        String result = HttpUtil.get(DOUYIN_API_PREFIX.concat(GET_USER_INFO).concat(urlParameter));
        log.info("result:{}", result);
        DouyinResult douyinResult = JSONObject.parseObject(result, DouyinResult.class);
        if (StringUtils.isBlank(douyinResult.getData())) {
            GetUserInfoResult getUserInfoResult = new GetUserInfoResult();
            getUserInfoResult.setDescription("data is null");
            return getUserInfoResult;
        }
        GetUserInfoResult getUserInfoResult = JSONUtil.toBean(douyinResult.getData(), GetUserInfoResult.class);
        if (getUserInfoResult.isNotSuccess()) {
            throw new DouyinException(getUserInfoResult.getDescription());
        }
        return getUserInfoResult;
    }


    /* 视频相关 */


    @Override
    public VideoListResult videoList(VideoList videoList) throws DouyinException {
        String urlParameter = getUrlParameter(videoList.getOpenId());
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("cursor", videoList.getCursor());
        jsonObject.put("count", videoList.getCount());
        String result = HttpUtil.get(DOUYIN_API_PREFIX.concat(VIDEO_LIST).concat(urlParameter), jsonObject);
        log.info("result:{}", result);
        DouyinResult douyinResult = JSONObject.parseObject(result, DouyinResult.class);
        String data = douyinResult.getData();
        if (StringUtils.isBlank(data)) {
            VideoListResult videoListResult = new VideoListResult();
            videoListResult.setList(Lists.newArrayList());
            videoListResult.setHasMore(false);
            return videoListResult;
        }
        VideoListResult videoListResult = JSONUtil.toBean(data, VideoListResult.class);
        if (videoListResult.isNotSuccess()) {
            throw new DouyinException(videoListResult.getDescription());
        }
        return videoListResult;
    }

    @Override
    public VideoListResult videoData(VideoData videoData) throws DouyinException {
        String urlParameter = getUrlParameter(videoData.getOpenId());
        JSONObject body = new JSONObject();
        JSONArray jsonArray = new JSONArray(videoData.getItemIds());
        body.put("item_ids", jsonArray);
        String result = HttpUtil.post(DOUYIN_API_PREFIX.concat(VIDEO_DATA).concat(urlParameter), body.toJSONString());
        log.info("result:{}", result);
        DouyinResult douyinResult = JSONObject.parseObject(result, DouyinResult.class);
        if (StringUtils.isBlank(douyinResult.getData())) {
            VideoListResult videoListResult = new VideoListResult();
            videoListResult.setDescription("data is null");
            videoListResult.setHasMore(false);
            videoListResult.setList(Lists.newArrayList());
            return videoListResult;
        }
        VideoListResult videoListResult = JSONUtil.toBean(douyinResult.getData(), VideoListResult.class);
        if (videoListResult.isNotSuccess()) {
            throw new DouyinException(videoListResult.getDescription());
        }

        return videoListResult;
    }


    /* 互动 */

    @Override
    public VideoCommentListResult videoCommentList(VideoCommentList videoCommentList) throws DouyinException {
        String urlParameter = getUrlParameter(videoCommentList.getOpenId());
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("cursor", videoCommentList.getCursor());
        jsonObject.put("count", videoCommentList.getCount());
        jsonObject.put("item_id", videoCommentList.getItemId());
        String result = HttpUtil.get(DOUYIN_API_PREFIX.concat(VIDEO_COMMENT_LIST).concat(urlParameter), jsonObject);
        log.info("result:{}", result);
        DouyinResult douyinResult = JSONObject.parseObject(result, DouyinResult.class);
        if (StringUtils.isBlank(douyinResult.getData())) {
            VideoCommentListResult videoCommentListResult = new VideoCommentListResult();
            videoCommentListResult.setDescription("data is null");
            videoCommentListResult.setHasMore(false);
            videoCommentListResult.setList(Lists.newArrayList());
            return videoCommentListResult;
        }
        VideoCommentListResult videoCommentListResult = JSONUtil.toBean(douyinResult.getData(), VideoCommentListResult.class);
        if (videoCommentListResult.isNotSuccess()) {
            throw new DouyinException(videoCommentListResult.getDescription());
        }

        return videoCommentListResult;
    }

    @Override
    public BaseResult videoCommentReply(VideoCommentReply videoCommentReply) throws DouyinException {
        String urlParameter = getUrlParameter(videoCommentReply.getOpenId());
        JSONObject bodyObject = new JSONObject();
        bodyObject.put("item_id", videoCommentReply.getItemId());
        bodyObject.put("comment_id", videoCommentReply.getCommentId());
        bodyObject.put("content", videoCommentReply.getContent());
        String body = JSONUtil.toJsonPrettyStr(bodyObject);
        String result = HttpUtil.post(DOUYIN_API_PREFIX.concat(VIDEO_COMMENT_REPLY).concat(urlParameter), body);
        log.info("result:{}", result);
        DouyinResult douyinResult = JSONObject.parseObject(result, DouyinResult.class);
        if (StringUtils.isBlank(douyinResult.getData())) {
            BaseResult baseResult = new BaseResult();
            baseResult.setDescription("data is null");
            return baseResult;
        }
        BaseResult baseResult = JSONUtil.toBean(douyinResult.getData(), BaseResult.class);
        if (baseResult.isNotSuccess()) {
            throw new DouyinException(baseResult.getDescription());
        }
        return baseResult;

    }

    @Override
    public BaseResult messageSend(SendMessage sendMessage) throws DouyinException {
        String urlParameter = getUrlParameter(sendMessage.getOpenId());

        JSONObject bodyObject = new JSONObject();
        bodyObject.put("to_user_id", sendMessage.getToUserId());
        bodyObject.put("message_type", sendMessage.getMessageType());
        bodyObject.put("content", sendMessage.getContent());
        String body = JSONUtil.toJsonPrettyStr(bodyObject);
        String result = HttpUtil.post(DOUYIN_API_PREFIX.concat(IM_MESSAGE_SEND).concat(urlParameter), body);
        log.info("result:{}", result);
        DouyinResult douyinResult = JSONObject.parseObject(result, DouyinResult.class);
        if (StringUtils.isBlank(douyinResult.getData())) {
            BaseResult baseResult = new BaseResult();
            baseResult.setDescription("data is null");
            return baseResult;
        }
        BaseResult baseResult = JSONUtil.toBean(douyinResult.getData(), BaseResult.class);
        if (baseResult.isNotSuccess()) {
            throw new DouyinException(baseResult.getDescription());
        }

        return baseResult;
    }

    /* 素材 */

    @Override
    public MediaUploadResult mediaUpload(UploadMedia uploadMedia) throws DouyinException {
        String urlParameter = getUrlParameter(uploadMedia.getOpenId());
        InputStream inputStream = uploadMedia.getInputStream();
        File tmpFile = null;
        try {
            tmpFile = FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), uploadMedia.getFileType());
            String result = HttpRequest.post(DOUYIN_API_PREFIX.concat(MEDIA_UPLOAD).concat(urlParameter)).form("media", tmpFile).execute().body();
            log.info("result:{}", result);
            DouyinResult douyinResult = JSONObject.parseObject(result, DouyinResult.class);
            if (StringUtils.isBlank(douyinResult.getData())) {
                MediaUploadResult mediaUploadResult = new MediaUploadResult();
                mediaUploadResult.setDescription("data is null");
                return mediaUploadResult;
            }
            MediaUploadResult mediaUploadResult = JSONUtil.toBean(douyinResult.getData(), MediaUploadResult.class);
            if(mediaUploadResult.isNotSuccess()){
                throw new DouyinException(mediaUploadResult.getDescription());
            }
            return mediaUploadResult;
        } catch (IOException e) {
            throw new DouyinException(e.getMessage());
        } finally {
            if (tmpFile != null) {
                boolean delete = tmpFile.delete();
                if (!delete) {
                    log.warn("file delete failed ,name:{}", tmpFile.getName());
                }
            }
        }
    }
}
